#include <nxos/ipc.h>
#include <nxos/signal.h>

#include "lwip/opt.h"

#if LWIP_SOCKET && (LWIP_IPV4 || LWIP_IPV6)

#include "lwip/sockets.h"
#include "lwip/sys.h"

#else
#error net server need LWIP_SOCKET && (LWIP_IPV4 || LWIP_IPV6) !
#endif

#include "netc.h"

#if 0
#define DEBUG_LOG(...) printf(__VA_ARGS__)
#else
#define DEBUG_LOG(...)
#endif

NX_PRIVATE NX_Error To_NX_Error(int err)
{
    switch (err)
    {
    case 0:             return NX_EOK;          /* No error, everything OK. */
    case ENOMEM:        return NX_ENOMEM;       /* Out of memory error. */
    case ENOBUFS:       return NX_ENORES;       /* Buffer error. */
    case ETIMEDOUT:     return NX_ETIMEOUT;     /* Timeout. */
    case EHOSTUNREACH:  return NX_EFAULT;       /* Routing problem. */
    case EINPROGRESS:   return NX_EBUSY;        /* Operation in progress */
    case EINVAL:        return NX_EINVAL;       /* Illegal value.  */
    case EWOULDBLOCK:   return NX_EAGAIN;       /* Operation would block. */
    case EADDRINUSE:    return NX_EBUSY;        /* Address in use. */
    case EALREADY:      return NX_EAGAIN;       /* Already connecting. */
    case EISCONN:       return NX_EBUSY;        /* Conn already established. */
    case ENOTCONN:      return NX_ENORES;       /* Not connected./Connection closed. */
    case -1:            return NX_EIO;          /* Low-level netif error */
    case ECONNABORTED:  return NX_EFAULT;       /* Connection aborted. */
    case ECONNRESET:    return NX_EAGAIN;       /* Connection reset. */
    case EIO:           return NX_EIO;          /* Illegal argument. */    
    default:            return NX_ERROR;        /* Unknown Error */    
    }
}

void SockCloseSignalHandler(NX_SignalInfo * info)
{
    long sock;

    if (info->signal != NX_SIGNAL_UOBJECT)
    {
        DEBUG_LOG("uobj %d not a socket!\n", info->signalValue);
        return;
    }
    sock = (long)info->signalValue;
    if (lwip_close(sock) != 0) {
        DEBUG_LOG("Net: do close err %d sock:%d\n",
            errno, sock);
    } else {
        DEBUG_LOG("Net: do close sock:%d\n", sock);
    }
}

NX_Size NET_Dispatch(NX_IpcMsg *ipcMsg)
{
    // DEBUG_LOG("-----> NET_Dispatch\n");
    NET_IpcReq *fr = (NET_IpcReq *)NX_IpcGetMsgData(ipcMsg);
	// printf("vfs: type:%d\n", fr->type);
	switch (fr->type)
	{
	case NET_REQ_SOCKET:
	{
		struct NET_SocketArgs *args = &fr->net_socket_args;
        errno = 0;
		long sock = lwip_socket(args->domain, args->type, args->protocol);
		if (sock < 0)
        {
            DEBUG_LOG("Net: do socket err %d, domain:%d, type:%d, protocol:%d\n",
                errno, args->domain, args->type, args->protocol);
        }

        long solt = NX_SoltInstall(ipcMsg->clientSlot, (void *)sock);
        DEBUG_LOG("Net: install object solt: %d, sock:%d\n", solt, sock);
        if (solt == NX_SOLT_INVALID_VALUE)
        {
            args->err = NX_ENORES;
            lwip_close(sock);
            return -1;
        }
        args->err = To_NX_Error(errno);

		return solt;
	}
    case NET_REQ_CLOSE:
	{
		struct NET_CloseArgs *args = &fr->net_close_args;
        if (args->socket < 0)
        {
            DEBUG_LOG("Net: do close invalid sock:%d\n",
                args->socket);
    		return NX_EINVAL;
        }

        errno = 0;

        /* uninstall uobject */
        NX_SoltUninstall(ipcMsg->clientSlot, args->socket);

		return To_NX_Error(errno);
	}
    case NET_REQ_BIND:
	{
		struct NET_BindArgs *args = &fr->net_bind_args;
        if (args->socket < 0)
        {
    		return NX_EINVAL;
        }

        long sock = (long)NX_SoltObject(ipcMsg->clientSlot, args->socket);
        if (sock < 0)
        {
    		return NX_EINVAL;
        }

        errno = 0;
		if (lwip_bind(sock, &args->addr, args->addrlen) != 0)
        {
            DEBUG_LOG("Net: do bind err %d, socket %d\n", errno, sock);
        }
        return To_NX_Error(errno);
	}
    case NET_REQ_LISTEN:
	{
		struct NET_ListenArgs *args = &fr->net_listen_args;

        if (args->socket < 0)
        {
    		return NX_EINVAL;
        }
        
        long sock = (long)NX_SoltObject(ipcMsg->clientSlot, args->socket);
        if (sock < 0)
        {
    		return NX_EINVAL;
        }
        
        errno = 0;
		if (lwip_listen(sock, args->backlog) != 0)
        {
            DEBUG_LOG("Net: do listen err %d on sock: %d backlog %d\n",
                errno, sock, args->backlog);
        }
        return To_NX_Error(errno);
	}
    case NET_REQ_ACCEPT:
	{
		struct NET_AcceptArgs *args = &fr->net_accept_args;
        long newSock;

        if (args->socket < 0)
        {
    		return NX_SOLT_INVALID_VALUE;
        }

        long sock = (long)NX_SoltObject(ipcMsg->clientSlot, args->socket);
        if (sock < 0)
        {
    		return NX_SOLT_INVALID_VALUE;
        }
        
        errno = 0;
        newSock = lwip_accept(sock, (void *)&args->addr, &args->addrlen);

        if (newSock < 0)
        {
            DEBUG_LOG("Net: accept err %d, socket: %d\n", errno, sock);
            args->err = To_NX_Error(errno);
            return newSock;
        }

        long solt = NX_SoltInstall(ipcMsg->clientSlot, (void *)newSock);
        DEBUG_LOG("Net: accept install client solt: %d, sock:%d\n", solt, newSock);
        if (solt == NX_SOLT_INVALID_VALUE)
        {
            args->err = NX_ENORES;
            lwip_close(newSock);
            return NX_SOLT_INVALID_VALUE;
        }
        args->err = To_NX_Error(errno);

        return solt;
	}
    case NET_REQ_CONNECT:
	{
		struct NET_ConnectArgs *args = &fr->net_connect_args;

        if (args->socket < 0)
        {
    		return NX_EINVAL;
        }

        long sock = (long)NX_SoltObject(ipcMsg->clientSlot, args->socket);
        if (sock < 0)
        {
    		return NX_EINVAL;
        }

        errno = 0;
        if (lwip_connect(sock, &args->addr, args->addrlen) != 0)
        {
            DEBUG_LOG("Net: do connect err %d on sock: %d, %d\n", errno, sock);
        }
        return To_NX_Error(errno);
	}
    case NET_REQ_SEND:
	{
		struct NET_SendArgs *args = &fr->net_send_args;
        int ret;
        if (args->socket < 0)
        {
    		return -1;
        }

        long sock = (long)NX_SoltObject(ipcMsg->clientSlot, args->socket);
        if (sock < 0)
        {
    		return NX_EINVAL;
        }

        errno = 0;
        ret = lwip_send(sock, args->buf, args->len, args->flags);

		if (ret < 0)
        {
            DEBUG_LOG("Net: do send err %d on sock: %d\n", errno, sock);
            args->err = To_NX_Error(errno);
        }
        return ret;
	}
    case NET_REQ_RECV:
	{
		struct NET_RecvArgs *args = &fr->net_recv_args;
        int ret;
        if (args->socket < 0)
        {
    		return -1;
        }

        long sock = (long)NX_SoltObject(ipcMsg->clientSlot, args->socket);
        if (sock < 0)
        {
    		return NX_EINVAL;
        }

        errno = 0;
        ret = lwip_recv(sock, args->buf, args->len, args->flags);

		if (ret < 0)
        {
            DEBUG_LOG("Net: do recv err %d on sock: %d\n", errno, sock);
            args->err = To_NX_Error(errno);
        }
        return ret;
	}
    case NET_REQ_SENDTO:
	{
		struct NET_SendtoArgs *args = &fr->net_sendto_args;
        int ret;
        if (args->socket < 0)
        {
    		return -1;
        }

        long sock = (long)NX_SoltObject(ipcMsg->clientSlot, args->socket);
        if (sock < 0)
        {
    		return NX_EINVAL;
        }

        errno = 0;
        ret = lwip_sendto(sock, args->buf, args->size, args->flags, 
            &args->to, args->tolen);

		if (ret < 0)
        {
            DEBUG_LOG("Net: do sendto err %d on sock: %d\n", errno, sock);
            args->err = To_NX_Error(errno);
        }
        return ret;
	}
    case NET_REQ_RECVFROM:
	{
		struct NET_RecvfromArgs *args = &fr->net_recvfrom_args;
        int ret;
        if (args->socket < 0)
        {
    		return -1;
        }

        long sock = (long)NX_SoltObject(ipcMsg->clientSlot, args->socket);
        if (sock < 0)
        {
    		return NX_EINVAL;
        }

        errno = 0;
        ret = lwip_recvfrom(sock, args->buf, args->size, args->flags, 
            &args->from, &args->fromlen);

		if (ret < 0)
        {
            DEBUG_LOG("Net: do recvfrom err %d on sock: %d\n", errno, sock);
            args->err = To_NX_Error(errno);
        }
        return ret;
	}
    case NET_REQ_SHUTDOWN:
	{
		struct NET_ShutdownArgs *args = &fr->net_shutdown_args;

        if (args->socket < 0)
        {
    		return NX_EINVAL;
        }

        long sock = (long)NX_SoltObject(ipcMsg->clientSlot, args->socket);
        if (sock < 0)
        {
    		return NX_EINVAL;
        }

        errno = 0;
		if (lwip_shutdown(sock, args->how) != 0)
        {
            DEBUG_LOG("Net: do shutdown err %d on sock: %d how %d\n",
                errno, sock, args->how);
        }
        return To_NX_Error(errno);
	}
    case NET_REQ_CONTROL:
	{
		struct NET_ControlArgs *args = &fr->net_control_args;
        long cmd;

        if (args->socket < 0)
        {
    		return NX_EINVAL;
        }
        errno = 0;

        long sock = (long)NX_SoltObject(ipcMsg->clientSlot, args->socket);
        if (sock < 0)
        {
    		return NX_EINVAL;
        }

        switch (args->cmd)
        {
        case NET_IONREAD:
            cmd = FIONREAD;
            break;
        case NET_IONBIO:
            cmd = FIONBIO;
            break;
        default:
            cmd = 0;
            break;
        }

		if (lwip_ioctl(sock, cmd, &args->argp) != 0)
        {
            DEBUG_LOG("Net: do shutdown err %d on sock: %d cmd %d\n",
                errno, sock, cmd);
        }
        return To_NX_Error(errno);
	}
    case NET_REQ_SETSOCKOPT:
	{
		struct NET_SetsockoptArgs *args = &fr->net_setsockopt_args;

        if (args->socket < 0)
        {
    		return NX_EINVAL;
        }
        
        long sock = (long)NX_SoltObject(ipcMsg->clientSlot, args->socket);
        if (sock < 0)
        {
    		return NX_EINVAL;
        }

        errno = 0;

		if (lwip_setsockopt(sock, args->level, args->optname, args->optval, args->optlen) != 0)
        {
            DEBUG_LOG("Net: do setsockopt err %d on sock: %d level %d, optname:%d\n",
                errno, sock, args->level, args->optname);
        }
        return To_NX_Error(errno);
	}
    case NET_REQ_GETSOCKOPT:
	{
		struct NET_GetsockoptArgs *args = &fr->net_getsockopt_args;

        if (args->socket < 0)
        {
    		return NX_EINVAL;
        }

        long sock = (long)NX_SoltObject(ipcMsg->clientSlot, args->socket);
        if (sock < 0)
        {
    		return NX_EINVAL;
        }

        errno = 0;

		if (lwip_getsockopt(sock, args->level, args->optname, args->optval, &args->optlen) != 0)
        {
            DEBUG_LOG("Net: do getsockopt err %d on sock: %d level %d, optname:%d\n",
                errno, sock, args->level, args->optname);
        }
        return To_NX_Error(errno);
	}
	default:
		break;
	}

    return 0;
}
